/*
 * @Author       : wang chao
 * @Date         : 2022-10-27 18:36:06
 * @LastEditors: wangchao
 * @LastEditTime: 2024-10-08 09:08:03
 * @FilePath: \JD-RTT-Driver\applications\u_port.c
 * @Description  :
 * Copyright 2022 BingShan, All Rights Reserved.
 */
#include "u_port.h"
#include "u_port_io.h"

#define DBG_TAG "PORT"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>

static struct rt_i2c_bus_device* i2c_bus = RT_NULL;
static rt_bool_t initialized = RT_FALSE;

static rt_err_t write_reg(struct rt_i2c_bus_device* bus, rt_uint8_t dev_addr, rt_uint8_t reg, rt_uint8_t* data)
{
    rt_uint8_t buf[3];
    struct rt_i2c_msg msgs;
    rt_uint32_t buf_size = 1;

    buf[0] = reg;
    if (data != RT_NULL)
    {
        buf[1] = data[0];
        buf[2] = data[1];
        buf_size = 3;
    }

    msgs.addr = dev_addr;
    msgs.flags = RT_I2C_WR;
    msgs.buf = buf;
    msgs.len = buf_size;

    if (rt_i2c_transfer(bus, &msgs, 1) == 1)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

static rt_err_t read_regs(struct rt_i2c_bus_device* bus, rt_uint8_t dev_addr, rt_uint8_t reg, rt_uint8_t len, rt_uint8_t* buf)
{
    struct rt_i2c_msg msgs;

    if (write_reg(bus, dev_addr, reg, RT_NULL) != RT_EOK)
    {
        return -RT_ERROR;
    }

    msgs.addr = dev_addr;
    msgs.flags = RT_I2C_RD;
    msgs.buf = buf;
    msgs.len = len;

    if (rt_i2c_transfer(bus, &msgs, 1) == 1)
    {
        return RT_EOK;
    }
    else
    {
        return -RT_ERROR;
    }
}

static void PCA555_init(const char* name)
{
    i2c_bus = (struct rt_i2c_bus_device*)rt_device_find(name);

    if (i2c_bus == RT_NULL)
    {
        LOG_E("can't find %s device.", name);
        return;
    }
    initialized = RT_TRUE;
}

static rt_uint16_t rt_port_temp_v1 = 0;
static rt_uint16_t rt_port_temp_v2 = 0;

static void PAC9555_set_port(rt_uint8_t* value, rt_uint8_t pin)
{
    if (pin >= 0 || pin < 16)
    {
        port_bit_write(rt_port_temp_v1, pin, 1);
        value[0] = get_low_byte(rt_port_temp_v1);
        value[1] = get_high_byte(rt_port_temp_v1);
    }
    if (pin >= 16)
    {
        pin = pin - 16;
        port_bit_write(rt_port_temp_v2, pin, 1);
        value[0] = get_low_byte(rt_port_temp_v2);
        value[1] = get_high_byte(rt_port_temp_v2);
    }
}

static void PAC9555_reset_port(rt_uint8_t* value, rt_uint8_t pin)
{
    if (pin >= 0 || pin < 16)
    {
        port_bit_write(rt_port_temp_v1, pin, 0);
        value[0] = get_low_byte(rt_port_temp_v1);
        value[1] = get_high_byte(rt_port_temp_v1);
    }
    if (pin >= 16)
    {
        pin = pin - 16;
        port_bit_write(rt_port_temp_v2, pin, 0);
        value[0] = get_low_byte(rt_port_temp_v2);
        value[1] = get_high_byte(rt_port_temp_v2);
    }
}

static rt_uint8_t current_do_port_state[32];

void PAC9555_dout_process(rt_uint8_t reg, rt_uint8_t val)
{
    rt_uint8_t temp[2] = {0, 0}, index = 0;

    if (!initialized)
    {
        PCA555_init(PCA555_I2C_BUS_NAME);
    }
    if (initialized)
    {
        index = reg;
        if (index < 16)
        {
            if (val == 1)
            {
                PAC9555_set_port(temp, index);
            }
            else
            {
                PAC9555_reset_port(temp, index);
            }
            write_reg(i2c_bus, PCA555_1_ADDR, 0x02, temp);
        }
        else
        {
            if (val == 1)
            {
                PAC9555_set_port(temp, index);
            }
            else
            {
                PAC9555_reset_port(temp, index);
            }
            write_reg(i2c_bus, PCA555_2_ADDR, 0x02, temp);
        }
        current_do_port_state[index] = val;
    }
    else
    {
        LOG_E("initialize sensor failed.");
    }
}

void do_port_operate(rt_uint8_t addr, rt_uint8_t val)
{
    if (addr < 8)
    {
        if (current_do_port_state[addr - 1] != val)
            PAC9555_dout_process(addr - 1, val);
    }
    else
    {
        if (current_do_port_state[addr] != val)
            PAC9555_dout_process(addr, val);
    }
}

rt_uint8_t get_do_chan_state_bit(rt_uint8_t addr)
{
    if (addr >= 0 && addr < 32)
    {
        if (addr < 8)
        {
            addr = addr - 1;
        }
        return current_do_port_state[addr];
    }
    return 0xff;
}

void PAC9555_dout_init(void)
{
    rt_uint8_t temp[2] = {0, 0}, index = 0;

    if (!initialized)
    {
        PCA555_init(PCA555_I2C_BUS_NAME);
    }
    if (initialized)
    {
        while (index < 32)
        {
            if (index < 16)
            {
                temp[0] = 0x00;
                temp[1] = 0x00;
                write_reg(i2c_bus, PCA555_1_ADDR, 0x06, temp);
                PAC9555_reset_port(temp, index);
                write_reg(i2c_bus, PCA555_1_ADDR, 0x02, temp);
            }
            else
            {
                temp[0] = 0x00;
                temp[1] = 0x00;
                write_reg(i2c_bus, PCA555_2_ADDR, 0x06, temp);
                PAC9555_reset_port(temp, index);
                write_reg(i2c_bus, PCA555_2_ADDR, 0x02, temp);
            }
            current_do_port_state[index] = 0;
            index++;
        }
    }
    else
    {
        LOG_E("initialize do port failed");
    }
}

static rt_uint16_t current_di_resp_low_word = 0xffff;
static rt_uint16_t current_di_resp_high_word = 0xffff;
static rt_uint8_t current_di_port_state[32] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};

rt_uint8_t get_din_state(rt_uint8_t pins)
{
    rt_uint8_t addr = pins - 1;
    if (addr >= 0 && addr < 32)
    {
        return current_di_port_state[addr];
    }
    return 0xff;
}

rt_uint32_t get_di_chan_state_word(void)
{
    return (rt_uint32_t)((current_di_resp_high_word << 16) | current_di_resp_low_word);
}

static void port_din_detect_thread_entry(void)
{
    rt_uint8_t temp[2] = {0}, mode[2];
    rt_uint16_t ret_value = 0, ix;
    if (!initialized)
    {
        PCA555_init(PCA555_I2C_BUS_NAME);
    }
    if (initialized)
    {
        mode[0] = 0xff;
        mode[1] = 0xff;
        write_reg(i2c_bus, PCA555_3_ADDR, 0x06, mode);

        while (1)
        {
            rt_thread_mdelay(120);
            read_regs(i2c_bus, PCA555_3_ADDR, 0x00, 2, temp);

            ret_value = temp[1] << 8 | temp[0];

            for (ix = 0; ix < 16; ix++)
            {
                current_di_port_state[ix] = port_bit_read(ret_value, ix);
                if (current_di_port_state[ix])
                {
                    port_bit_write(current_di_resp_low_word, ix, 1);
                }
                else
                {
                    port_bit_write(current_di_resp_low_word, ix, 0);
                }
            }

            rt_thread_mdelay(120);
            read_regs(i2c_bus, PCA555_4_ADDR, 0x00, 2, temp);

            ret_value = temp[1] << 8 | temp[0];

            for (ix = 0; ix < 16; ix++)
            {
                current_di_port_state[ix + 16] = port_bit_read(ret_value, ix);

                if (current_di_port_state[ix + 16])
                {
                    port_bit_write(current_di_resp_high_word, ix, 1);
                }
                else
                {
                    port_bit_write(current_di_resp_high_word, ix, 0);
                }
            }
        }
    }
    else
    {
        LOG_E("initialize di port failed");
    }
}

int bms_port_din_detect_thread(void)
{
    rt_thread_t thread_id = RT_NULL;
    thread_id = rt_thread_create("di", port_din_detect_thread_entry, RT_NULL, B_DI_STACK_SIZE, B_DI_THREAD_PRIORITY, 20);
    if (thread_id != RT_NULL)
    {
        rt_thread_startup(thread_id);
    }
    else
    {
        goto __exit;
    }
    return RT_EOK;

__exit:
    if (thread_id)
        rt_thread_delete(thread_id);
    return -RT_ERROR;
}

INIT_COMPONENT_EXPORT(PAC9555_dout_init);

// DO、DI端口实例化

SYSTEM_DO_HANDLE APP_DO;

void APP_DO_EVENT_REGISTER(void)
{
    APP_DO.SET = do_port_operate;
    APP_DO.GET = get_do_chan_state_bit;
}

INIT_APP_EXPORT(APP_DO_EVENT_REGISTER);

SYSTEM_DI_HANDLE APP_DI;

void APP_DI_EVENT_REGISTER(void)
{
    APP_DI.GET = get_din_state;
    APP_DI.GET_WORD = get_di_chan_state_word;
}

// 判断急停按钮是否触发
rt_bool_t is_push_emergency_button(void)
{
    rt_uint8_t ret = get_din_state(DI_RESP_E_STOP_PIN);
    // 默认急停市场
    if (ret == 0x1)
    {
        return RT_TRUE;
    }
    return RT_FALSE;
}
